package su.msk.jet.team01.chatik.server.db;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jolbox.bonecp.BoneCP;
import com.jolbox.bonecp.BoneCPConfig;

public final class DatabaseAdapter {

	private static Logger logger = LoggerFactory
			.getLogger(DatabaseAdapter.class);

	private static BoneCP connectionPool = null;

	private static class Holder {
		public static final DatabaseAdapter instance = new DatabaseAdapter();
	}
	
	public static DatabaseAdapter getInstance() {
		return Holder.instance;
	}
	
	private static void setConnectionPool(BoneCP pool) {
		connectionPool = pool;
	}

	public void connect() throws SQLException{
		BoneCPConfig config = new BoneCPConfig();
		
		config.setConfigFile("bonecp-config.xml");
		
		setConnectionPool(new BoneCP(config));
	}

	public List<Map<String, Object>> execute(final String sql,
			final Object... args) throws SQLException {
		List<Map<String, Object>> table = null;
		PreparedStatement stmt = null;
		Connection conn = null;
		ResultSet data = null;

		try {
			conn = connectionPool.getConnection();
			stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
			prepareStatement(stmt, args);
			stmt.executeUpdate();
			data = stmt.getGeneratedKeys();
			table = DatabaseResultConverter.convertResults(data);
		} finally {
			close(data, stmt, conn);
		}

		return table;
	}

	public int executeInsert(final String sql, final Object... args)
			throws SQLException {
		List<Map<String, Object>> res;
		int id;

		res = execute(sql, args);
		id = ((BigDecimal) res.get(0).entrySet().iterator().next().getValue())
				.intValue();

		return id;
	}

	public List<Map<String, Object>> executeQuery(final String sql,
			final Object... args) throws SQLException {
		List<Map<String, Object>> table = null;
		PreparedStatement stmt = null;
		ResultSet data = null;
		Connection conn = null;

		try {
			conn = connectionPool.getConnection();
			stmt = conn.prepareStatement(sql);
			prepareStatement(stmt, args);
			data = stmt.executeQuery();
			table = DatabaseResultConverter.convertResults(data);
		} finally {
			close(data, stmt, conn);
		}

		return table;
	}

	private void close(final AutoCloseable... closeables) {
		for (AutoCloseable closeable : closeables) {
			if (closeable != null) {
				try {
					closeable.close();
				} catch (Exception ex) {
					logger.error("Can't close object of type "
							+ closeable.getClass() + ". Error message: "
							+ ex.getMessage());
				}
			}
		}
	}

	private void prepareStatement(final PreparedStatement stmt,
			final Object... args) throws SQLException {
		for (int i = 0; i < args.length; i++) {
			stmt.setObject(i + 1, args[i]);
		}
	}

}
